package com.test.aop;

import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * AspectJ表达式切点类，用于解析和匹配AspectJ切点表达式
 * 它实现了Pointcut, ClassFilter, MethodMatcher接口，以支持切点表达式的解析和匹配功能
 */
public class AspectjExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher{
    // 支持的切点原始类型集合
    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();

    // 初始化支持的切点原始类型
    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
    }

    // 切点表达式字符串
    private String expression;

    // 解析后的切点表达式对象
    private PointcutExpression pointcutExpression;

    // 切点类加载器
    private final ClassLoader pointcutClassLoader;

    /**
     * 默认构造函数
     */
    public AspectjExpressionPointcut() {
        this(null,null);
    }

    /**
     * 单参数构造函数
     *
     * @param expression 切点表达式字符串
     */
    public AspectjExpressionPointcut(String expression) {
        this(expression,null);
    }

    /**
     * 双参数构造函数
     *
     * @param expression 切点表达式字符串
     * @param pointcutClassLoader 切点类加载器
     */
    public AspectjExpressionPointcut(String expression, ClassLoader pointcutClassLoader) {
        this.expression = expression;
        this.pointcutClassLoader = (pointcutClassLoader != null ? pointcutClassLoader : this.getClass().getClassLoader());
        if (this.expression != null){
            buildPointcutExpression();
        }
    }

    /**
     * 设置切点表达式字符串
     *
     * @param expression 切点表达式字符串
     */
    public void setExpression(String expression){
        this.expression = expression;
        buildPointcutExpression();
    }

    /**
     * 获取切点表达式字符串
     *
     * @return 切点表达式字符串
     */
    public String getExpression() {
        return this.expression;
    }

    /**
     * 构建切点表达式对象
     * 如果表达式为空，则抛出IllegalArgumentException异常
     */
    private void buildPointcutExpression() {
        if (this.expression == null){
            throw new IllegalArgumentException("表达式不能为空");
        }
        PointcutParser parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.pointcutClassLoader);
        this.pointcutExpression = parser.parsePointcutExpression(this.expression);
    }

    /**
     * 匹配类是否符合切点表达式
     *
     * @param clazz 要匹配的类
     * @return 如果类符合切点表达式则返回true，否则返回false
     */
    @Override
    public boolean matches(Class<?> clazz) {
        checkReadyToMatch();
        return this.pointcutExpression.couldMatchJoinPointsInType(clazz);
    }

    /**
     * 匹配方法执行是否符合切点表达式
     *
     * @param method 要匹配的方法
     * @param targetClass 目标类
     * @return 如果方法执行符合切点表达式则返回true，否则返回false
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        checkReadyToMatch();
        ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
        return shadowMatch.alwaysMatches();
    }

    /**
     * 判断切点表达式是否为运行时匹配
     *
     * @return 如果切点表达式可能需要动态测试则返回true，否则返回false
     */
    @Override
    public boolean isRuntime() {
        checkReadyToMatch();
        return pointcutExpression.mayNeedDynamicTest();
    }

    /**
     * 匹配方法执行是否符合切点表达式，考虑运行时参数
     *
     * @param method 要匹配的方法
     * @param targetClass 目标类
     * @param args 方法参数
     * @return 如果方法执行符合切点表达式则返回true，否则返回false
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        checkReadyToMatch();
        ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
        return shadowMatch.alwaysMatches();
    }

    /**
     * 获取类过滤器
     *
     * @return 类过滤器对象
     */
    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    /**
     * 获取方法匹配器
     *
     * @return 方法匹配器对象
     */
    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }

    /**
     * 检查是否准备好进行匹配
     * 如果切点表达式对象为空，则抛出IllegalStateException异常
     */
    private void checkReadyToMatch() {
        if (this.pointcutExpression == null) {
            throw new IllegalStateException("表达式不能为空");
        }
    }
}
